import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @Description mybatis 拦截器，打印完整sql 只针对传参时候
 *
 * @author gtinlian
 */
@Component
@Intercepts({
        @Signature(type = ParameterHandler.class, method = "setParameters", args = { PreparedStatement.class })
})
public class MybatisSqlPrintPlugins implements Interceptor {
    Logger log = LoggerFactory.getLogger("");

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        ParameterHandler handler = (ParameterHandler) invocation.getTarget();
        Object[] args = invocation.getArgs();
        Object result = invocation.getMethod().invoke(handler, args);
        log.info(" {}\n{}", getMethod(invocation), getSql(invocation));
        return result;
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof ParameterHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {
    }

    /**
     * @author gtinlian
     *         获取sql 语句的方法
     *
     * @param invocation
     * @return
     * @throws NoSuchMethodException
     * @throws SQLException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private String getSql(Invocation invocation)
            throws NoSuchMethodException, SQLException, InvocationTargetException, IllegalAccessException {
        PreparedStatement preparedStatement = (PreparedStatement) invocation.getArgs()[0];
        com.mysql.jdbc.PreparedStatement unwrap = preparedStatement.unwrap(com.mysql.jdbc.PreparedStatement.class);
        Method method = com.mysql.jdbc.PreparedStatement.class.getDeclaredMethod("asSql");
        method.setAccessible(true);
        String sql = (String) method.invoke(unwrap);
        return sql.replaceAll("[\\s]+", " ");
    }

    /**
     * @author gtinlian
     *         获取执行方法名
     *
     * @param invocation
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private String getMethod(Invocation invocation) throws NoSuchFieldException, IllegalAccessException {
        DefaultParameterHandler target = (DefaultParameterHandler) invocation.getTarget();
        Field field = DefaultParameterHandler.class.getDeclaredField("mappedStatement");
        field.setAccessible(true);
        MappedStatement mappedStatement = (MappedStatement) field.get(target);
        return mappedStatement.getId();
    }
}